package org.wu.framework.core.annotation;

import org.wu.framework.core.utils.ObjectUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

public class AnnotationsScanner {


    // 缓存 注解
    public static ConcurrentHashMap<AnnotatedElement, MergedAnnotationElement> cacheAnnotatedElement = new ConcurrentHashMap<>(128);

    // 缓存防止递归
    public static LinkedHashSet<AnnotatedElement> cacheAnnotatedElementList = new LinkedHashSet<>();





    public static MergedAnnotationElement scan(AnnotatedElement annotatedElement) {
        if (cacheAnnotatedElement.containsKey(annotatedElement)) {
            return cacheAnnotatedElement.get(annotatedElement);
        }
        // 如果是 class
        MergedAnnotationElement mergedAnnotationElement = null;
        if (annotatedElement instanceof Class<?> clazz) {
            if (clazz.isAnnotation()) {
                // 过滤 java.lang.annotation 包下的注解
                mergedAnnotationElement = scanAnnotation0((Class<? extends Annotation>) clazz);
            } else {
                mergedAnnotationElement = scanClass(clazz);
            }

        } else if (annotatedElement instanceof Method method) {
            mergedAnnotationElement = scanMethod(method);

        } else if (annotatedElement instanceof Field field) {
            mergedAnnotationElement = scanField(field);
        } else {
            mergedAnnotationElement = scanElement(annotatedElement);
        }
        if (mergedAnnotationElement != null) {
            cacheAnnotatedElement.put(annotatedElement, mergedAnnotationElement);
        }


        return mergedAnnotationElement;

    }

    /**
     * 扫描字段上的注解
     *
     * @param field 字段
     * @return 字段上的注解
     */
    private static MergedAnnotationElement scanField(Field field) {
        MergedAnnotationElement mergedAnnotationElement = new MergedAnnotationElement();
        mergedAnnotationElement.setElement(field);
        List<MergedAnnotationElement> mergedAnnotationElementList = Arrays.stream(
                        field.getAnnotations())
                .map(AnnotationsScanner::scanAnnotation).toList();
        mergedAnnotationElement.setMergedAnnotationClassList(mergedAnnotationElementList);
        return mergedAnnotationElement;
    }

    /**
     * 扫描注解
     *
     * @param annotation 注解
     */
    public static MergedAnnotationElement scanAnnotation(Annotation annotation) {
        Class<? extends Annotation> annotationType = annotation.annotationType();
        MergedAnnotationElement mergedAnnotationElement = scanAnnotation0(annotationType);
        mergedAnnotationElement.setSource(annotation);
        return mergedAnnotationElement;


    }

    /**
     * 解析注解上的注解
     *
     * @param annotationType 注解
     * @return 注解上的注解信息
     */
    private static MergedAnnotationElement scanAnnotation0(Class<? extends Annotation> annotationType) {

        // 防止注解递归
//        cacheAnnotatedElementList.add(annotationType);
        MergedAnnotationElement mergedAnnotationElement = new MergedAnnotationElement();


        // 获取注解上的注解
        List<MergedAnnotationElement> mergedAnnotationElementList = new java.util.ArrayList<>(
                Arrays.stream(annotationType.getAnnotations())
                        .filter(annotation -> !annotation.annotationType().equals(annotationType))// 过滤跟自己一样的注解
                        .filter(annotation1 -> !AnnotationFilter.PLAIN.matches(annotation1.annotationType())) // 过滤 java.lang
//                        .filter(annotation1 -> !cacheAnnotatedElementList.contains(annotation1.annotationType())) // 过滤 递归注解
                        .map(AnnotationsScanner::scanAnnotation)
                        .filter(Objects::nonNull)
                        .toList());
        if (Arrays.stream(annotationType.getAnnotations()).anyMatch(annotation -> annotation.annotationType().equals(annotationType))) {
            mergedAnnotationElementList.add(mergedAnnotationElement);
        }

        mergedAnnotationElement.setElement(annotationType);
        // 获取注解中的方法
        List<Method> methods = Arrays.stream(annotationType.getMethods()).toList();
        mergedAnnotationElement.setAnnotationMethodList(methods);
        // 获取注解方法上的注解
        List<MergedAnnotationElement> methodAnnotationClasses = methods
                .stream()
                .map(AnnotationsScanner::scanMethod).filter(Objects::nonNull).toList();

        mergedAnnotationElementList.addAll(methodAnnotationClasses);

        mergedAnnotationElement.setMergedAnnotationClassList(mergedAnnotationElementList);
        return mergedAnnotationElement;
    }

    /**
     * 解析元素上的注解
     *
     * @param annotatedElement 元素
     * @return 返回元素上的注解
     */
    private static MergedAnnotationElement scanElement(AnnotatedElement annotatedElement) {

        // 处理注解上的注解

//        cacheAnnotatedElement.put(annotatedElement,);
        return null;
    }

    /**
     * 解析方法上的注解
     *
     * @param method 方法
     * @return 返回方法上的注解信息
     */
    private static MergedAnnotationElement scanMethod(Method method) {
        MergedAnnotationElement mergedAnnotationElement = new MergedAnnotationElement();
        // 获取注解方法上的 注解信息
        List<MergedAnnotationElement> mergedAnnotationElementList = Arrays
                .stream(method.getAnnotations())
                .filter(annotation1 -> !AnnotationFilter.PLAIN.matches(annotation1.annotationType())) // 过滤 java.lang
                .map(AnnotationsScanner::scanAnnotation)
                .toList();
        if (ObjectUtils.isEmpty(mergedAnnotationElementList)) {
            return null;
        }
        mergedAnnotationElement.setElement(method);
        mergedAnnotationElement.setMergedAnnotationClassList(mergedAnnotationElementList);

        return mergedAnnotationElement;
    }

    /**
     * 解析class 上的注解
     *
     * @param clazz class
     * @return class 上的注解
     */
    private static MergedAnnotationElement scanClass(Class<?> clazz) {

        MergedAnnotationElement mergedAnnotationElement = new MergedAnnotationElement();
        mergedAnnotationElement.setElement(clazz);
        // 获取class上的注解
        List<MergedAnnotationElement> mergedAnnotationElementList = Arrays
                .stream(clazz.getAnnotations())
                .map(AnnotationsScanner::scanAnnotation)
                .toList();
        mergedAnnotationElement.setMergedAnnotationClassList(mergedAnnotationElementList);

        return mergedAnnotationElement;

    }

    /**
     * 扫描是否有指定注解
     *
     * @param annotatedElement 元素
     * @param annotation       指定注解类型
     * @param <A>
     * @return 布尔类型
     */
    public static <A extends Annotation> boolean scanHasElementAnnotation(AnnotatedElement annotatedElement, Class<A> annotation) {

        A elementAnnotation = annotatedElement.getAnnotation(annotation);
        if (elementAnnotation != null) {
            return true;
        }

        MergedAnnotationElement scan = scan(annotatedElement);
        // 深度解析
        return hasAnnotationInMergedAnnotationElement(scan, annotation);
    }

    /**
     * 递归解析
     *
     * @param mergedAnnotationElement 元素
     * @param annotation       指定注解类型
     * @param <A>
     * @return 布尔类型
     */

    protected static <A extends Annotation> boolean hasAnnotationInMergedAnnotationElement(MergedAnnotationElement mergedAnnotationElement, Class<A> annotation) {

        if (mergedAnnotationElement == null) {
            return false;
        }
        if (mergedAnnotationElement.getElement().equals(annotation)) {
            return true;
        } else {
            return mergedAnnotationElement.getMergedAnnotationClassList().stream()
                    .anyMatch(a -> hasAnnotationInMergedAnnotationElement(a, annotation));
        }

    }
}
